package io.hepu.robotize.interceptor;

import io.hepu.robotize.aop.RedisId;
import io.hepu.robotize.auth.JRedis;
import io.hepu.robotize.util.JAnnotation;
import io.hepu.robotize.util.JId;
import io.hepu.robotize.util.Limits;
import io.hepu.robotize.util.Reflects;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Objects;

@Component
@Intercepts(@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }))
public class IdInterceptor implements Interceptor {
    private static final Logger LOG = LoggerFactory.getLogger(IdInterceptor.class);
    // 实体主键字段
    private final static String ID_FIELD = "id";
    private final static String ID_CLASS = "idClass";

    // 插入数据前设置主键ID
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 插入的对象实体
        Object parameter = invocation.getArgs()[1];
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        if (Objects.isNull(parameter) || !sqlCommandType.equals(SqlCommandType.INSERT)) {
            return invocation.proceed();
        }
        Object id = Reflects.get(parameter, ID_FIELD);
        Class<?> idClass = (Class<?>) Reflects.get(parameter, ID_CLASS);
        if (Limits.isEmpty(id) && Limits.nonNull(idClass)) {
            RedisId redisId = JAnnotation.ifPresent(parameter.getClass(), RedisId.class);
            // If annotation exists, use cached id; otherwise, generate a random one.
            id = Limits.nonNull(redisId) ? resolveId(redisId, idClass) : JId.id(idClass);
            LOG.info("Generate [Primary Key]: {} => {}", redisId.table(), id);
            Reflects.set(parameter, ID_FIELD, id);
        }
        return invocation.proceed();
    }

    private Object resolveId(RedisId redisId, Class<?> idType) {
        String cacheKey = redisId.prefix() + redisId.table();
        return idType.isAssignableFrom(String.class) ? JRedis.stringId(cacheKey) : JRedis.longId(cacheKey);
    }

    @Override
    public Object plugin(Object target) {
        return target instanceof Executor ? (Plugin.wrap(target, this)) : target;
    }

}
